// ==UserScript== // @name TikTok 小助手 // @namespace http://tampermonkey.net/ // @version 5.21 // @description 获取 ttk 数据! // @author // @match https://www.tiktok.com/* // @grant GM_registerMenuCommand // @grant GM_setValue // @grant GM_getValue // @grant GM_getResourceText // @grant GM_addStyle // @icon https://iili.io/dy5xjOg.jpg // @require https://code.jquery.com/jquery-3.6.0.min.js // @require https://cdnjs.cloudflare.com/ajax/libs/toastify-js/1.12.0/toastify.min.js // @resource TOASTIFY_CSS https://cdn.jsdelivr.net/npm/toastify-js/src/toastify.min.css // ==/UserScript== (function() { 'use strict'; // 加载 Toastify.js 的 CSS const toastifyCSS = GM_getResourceText('TOASTIFY_CSS'); GM_addStyle(toastifyCSS); // 注入按钮样式到页面 injectButtonStyles(); let currentUrl = window.location.href; let retryCount = 0; let parsedData = {}; // 获取设置值,默认值为 false let autoShowDataPanel = GM_getValue('autoShowDataPanel', false); // 在脚本菜单中添加选项以设置是否自动弹出数据面板 GM_registerMenuCommand('切换自动弹出数据面板', () => { autoShowDataPanel = !autoShowDataPanel; GM_setValue('autoShowDataPanel', autoShowDataPanel); alert(`自动弹出数据面板已${autoShowDataPanel ? '启用' : '禁用'}`); }); // 注入按钮样式到页面 function injectButtonStyles() { if (document.getElementById('buttonStyles')) { return; // Styles already injected } const styleElement = document.createElement('style'); styleElement.id = 'buttonStyles'; styleElement.type = 'text/css'; styleElement.textContent = ` .button-85 { padding: 0.6em 0.8em; border: none; outline: none; color: rgb(255, 255, 255); background: #111; cursor: pointer; position: fixed; z-index: 10001; border-radius: 10px; user-select: none; -webkit-user-select: none; touch-action: manipulation; } .button-85:before { content: ""; background: linear-gradient( 45deg, #ff0000, #ff7300, #fffb00, #48ff00, #00ffd5, #002bff, #7a00ff, #ff00c8, #ff0000 ); position: absolute; top: -2px; left: -2px; background-size: 400%; z-index: -1; filter: blur(5px); -webkit-filter: blur(5px); width: calc(100% + 4px); height: calc(100% + 4px); animation: glowing-button-85 20s linear infinite; transition: opacity 0.3s ease-in-out; border-radius: 10px; } @keyframes glowing-button-85 { 0% { background-position: 0 0; } 50% { background-position: 400% 0; } 100% { background-position: 0 0; } } .button-85:after { z-index: -1; content: ""; position: absolute; width: 100%; height: 100%; background: #222; left: 0; top: 0; border-radius: 10px; } `; document.head.appendChild(styleElement); } // 创建用于显示数据面板的按钮 function createButton() { // 创建数据按钮 let dataButton = document.querySelector('#tiktokDataButton'); if (!dataButton) { dataButton = document.createElement('button'); dataButton.id = 'tiktokDataButton'; dataButton.className = 'button-85'; dataButton.innerHTML = '🤓'; dataButton.style.top = '10px'; dataButton.style.right = '340px'; dataButton.addEventListener('click', () => { toggleDataDisplay(); }); document.body.appendChild(dataButton); console.log('Data button created and appended to the page.'); } // 创建刷新按钮 let refreshButton = document.querySelector('#tiktokRefreshButton'); if (!refreshButton) { refreshButton = document.createElement('button'); refreshButton.id = 'tiktokRefreshButton'; refreshButton.className = 'button-85'; refreshButton.innerHTML = '🔄'; refreshButton.style.top = '10px'; refreshButton.style.right = '410px'; refreshButton.addEventListener('click', () => { console.log('Manual refresh button clicked.'); retryCount = 0; currentUrl = window.location.href; // 提取数据并在提取完成后更新显示 extractStats(true); // 更新数据面板内容而不关闭面板 setTimeout(() => { let dataContainer = document.querySelector('#tiktokDataContainer'); if (dataContainer) { updateDataContainerContent(dataContainer, parsedData); } else { // 如果面板不存在,则显示它 toggleDataDisplay(); } }, 500); // 添加适当的延迟以确保数据提取完成 }); document.body.appendChild(refreshButton); console.log('Refresh button created and appended to the page.'); } } // 切换数据面板的显示和隐藏 function toggleDataDisplay() { console.log('toggleDataDisplay called'); let dataContainer = document.querySelector('#tiktokDataContainer'); if (dataContainer) { // 如果面板已存在,则移除 dataContainer.style.transform = 'translateX(100%)'; dataContainer.style.opacity = '0'; setTimeout(() => { dataContainer.remove(); }, 500); return; } // 创建新的数据面板 dataContainer = document.createElement('div'); dataContainer.id = 'tiktokDataContainer'; dataContainer.style.transition = 'transform 0.5s ease-in-out, opacity 0.5s ease-in-out'; dataContainer.style.transform = 'translateX(100%)'; dataContainer.style.opacity = '0'; dataContainer.style.position = 'fixed'; dataContainer.style.top = '60px'; dataContainer.style.right = '20px'; dataContainer.style.width = '300px'; dataContainer.style.maxHeight = '400px'; dataContainer.style.overflowY = 'auto'; dataContainer.style.backgroundColor = '#ffffff'; dataContainer.style.border = '1px solid #ccc'; dataContainer.style.borderRadius = '8px'; dataContainer.style.boxShadow = '0px 0px 10px rgba(0, 0, 0, 0.1)'; dataContainer.style.padding = '15px'; dataContainer.style.zIndex = '10000'; const title = document.createElement('h2'); title.textContent = '🎉 好!发现了'; title.style.color = '#000000'; title.style.marginBottom = '10px'; dataContainer.appendChild(title); createJsonElement(parsedData, dataContainer); document.body.appendChild(dataContainer); setTimeout(() => { dataContainer.style.transform = 'translateX(0)'; dataContainer.style.opacity = '1'; }, 10); } function updateDataContainerContent(container, data) { // 清空现有内容 container.innerHTML = ''; const title = document.createElement('h2'); title.textContent = '🎉 好!发现了'; title.style.color = '#000000'; title.style.marginBottom = '10px'; container.appendChild(title); createJsonElement(data, container); } // 创建用于显示数据的元素 function createJsonElement(data, container) { function createDataRow(label, value, copyValue) { const row = document.createElement('div'); row.style.display = 'flex'; row.style.alignItems = 'center'; row.style.marginBottom = '10px'; const textElement = document.createElement('div'); textElement.textContent = `${label}: ${value}`; textElement.style.color = '#000000'; const copyIcon = document.createElement('img'); copyIcon.src = base64CopyIcon; copyIcon.style.cursor = 'pointer'; copyIcon.style.width = '20px'; copyIcon.style.marginLeft = '10px'; copyIcon.addEventListener('click', (event) => { event.preventDefault(); navigator.clipboard.writeText(copyValue).then(() => { showNotification('已复制到剪贴板: ' + copyValue); }).catch(err => { console.error('复制失败: ', err); }); }); row.appendChild(textElement); row.appendChild(copyIcon); container.appendChild(row); } const accountName = window.location.pathname.split('/')[1].replace('@', ''); const base64CopyIcon = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAYUlEQVR4nGNgGE7Am4GB4QkDA8N/MjFB8JgCw/8TNp4EheQCulvgTWacgILakxgLKImTR8RYAOP7kIhxBvWoBT6jQeQzmor+0zqjoYOhb8Fjahd26MCTTEtAhnsQY8HQAABVctFxfxXV5QAAAABJRU5ErkJggg=="; createDataRow('账户名', accountName, accountName); createDataRow('粉丝数', data.followerCount || '未知', data.followerCount || '未知'); const fields = { 'diggCount': '点赞数', 'playCount': '播放数', 'commentCount': '评论数', 'shareCount': '分享数', 'collectCount': '收藏数', 'createTime': '创建时间' }; for (const [field, label] of Object.entries(fields)) { if (data.hasOwnProperty(field)) { let value = data[field]; let copyValue = value; if (field === 'createTime') { if (value === 0) continue; const date = new Date(value * 1000); value = date.toLocaleString(); copyValue = date.toISOString().slice(0, 19).replace('T', ' '); } createDataRow(label, value, copyValue); } } } // 提取视频统计信息 function extractStats(isManual = false) { fetch(window.location.href) .then(response => response.text()) .then(responseText => { const scriptMatch = responseText.match(/